package cn.tannn.surenessdemo.redis.service.impl;

import cn.tannn.surenessdemo.constant.RedisKeyConstant;
import cn.tannn.surenessdemo.redis.service.RedisService;
import cn.tannn.surenessdemo.user.dao.AuthResourceDao;
import cn.tannn.surenessdemo.user.dao.AuthUserDao;
import cn.tannn.surenessdemo.enums.ExceptionEnum;
import cn.tannn.surenessdemo.exception.ExpiredRedisException;
import cn.tannn.surenessdemo.sureness.processor.RedisAccount;
import cn.tannn.surenessdemo.user.dao.po.AccountOwnRolesPO;
import cn.tannn.surenessdemo.util.CustomUtil;
import com.usthe.sureness.processor.exception.DisabledAccountException;
import com.usthe.sureness.processor.exception.ExcessiveAttemptsException;
import com.usthe.sureness.provider.SurenessAccount;
import com.usthe.sureness.util.SurenessCommonUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis
 *
 * @author tnnn
 * @version V1.0
 * @date 2022-07-22 14:08
 */
@Service
public class RedisServiceImpl implements RedisService {

    /**
     * reids
     */
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 用户
     */
    private final AuthUserDao authUserDao;



    private final AuthResourceDao authResourceDao;

    /**
     * 用户登录有效时间，默认为一天
     */
    @Value("${tn.redis.expire.user:86400}")
    private Integer userRedisExpireTime;

    public RedisServiceImpl(AuthUserDao authUserDao,
                            AuthResourceDao authResourceDao) {
        this.authUserDao = authUserDao;
        this.authResourceDao = authResourceDao;
    }


    @Override
    public void storageUserRole(String username) {
        // 用户角色存入 redis
        String roleRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_ROLE_FOLDER, username);
        redisTemplate.boundHashOps(roleRedisFolder).put(username, authUserDao.findAccountOwnRoles(username));
        // 永不过期
        redisTemplate.persist(roleRedisFolder);
    }

    @Override
    public void storageUserRole(String username, List<String> roles) {
        // 用户角色存入 redis
        String roleRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_ROLE_FOLDER, username);
        redisTemplate.boundHashOps(roleRedisFolder).put(username, roles);
        // 永不过期
        redisTemplate.persist(roleRedisFolder);

    }

    @Override
    public void refreshRoleAll() {
        // 删除所有用户的角色
        redisTemplate.delete(RedisKeyConstant.REDIS_USER_ROLE_FOLDER+":*");

        List<AccountOwnRolesPO> ownRoles = authUserDao.findAccountOwnRoles();
        Map<String, List<AccountOwnRolesPO>> collect = ownRoles.stream().collect(Collectors.groupingBy(AccountOwnRolesPO::getUsername));
        collect.forEach((k,v) ->{
            // 新增所有用户的角色
            String roleRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_ROLE_FOLDER, k);
            redisTemplate.boundHashOps(roleRedisFolder).put(k, v.stream().map(AccountOwnRolesPO::getRoleCode).collect(Collectors.toList()));
            // 永不过期
            redisTemplate.persist(roleRedisFolder);
        });


    }

    @Override
    public List<String> loadUserRole(String username) {
        String roleRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_ROLE_FOLDER, username);
        Object roles = redisTemplate.boundHashOps(roleRedisFolder).get(username);
        if (Objects.isNull(roles)) {
            return authUserDao.findAccountOwnRoles(username);
        } else {
            return (List<String>) roles;
        }
    }

    @Override
    public void storageUserToken(String username, String token) {
        String loginRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_LOGIN_FOLDER, username);
        redisTemplate.boundHashOps(loginRedisFolder).put(username, token);
        // 设置过期时间（秒
        redisTemplate.expire(loginRedisFolder, userRedisExpireTime, TimeUnit.SECONDS);
    }


    @Override
    public void refreshUserToken(String username) {
        String loginRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_LOGIN_FOLDER, username);
        // 设置过期时间（秒
        redisTemplate.expire(loginRedisFolder, userRedisExpireTime, TimeUnit.SECONDS);
    }

    @Override
    public void removeUserToken(String username) {
        String redisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_LOGIN_FOLDER, username);
        redisTemplate.delete(redisFolder);
    }

    @Override
    public String verifyUserToken(String username) {
        String loginRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_LOGIN_FOLDER, username);
        // redis 中比对 token 正确性
        String tokenRedis = (String) redisTemplate.boundHashOps(loginRedisFolder).get(username);
        if (StringUtils.isBlank(tokenRedis)) {
            throw new ExpiredRedisException(ExceptionEnum.REDIS_EXPIRED_USER);
        }
        return tokenRedis;
    }

    @Override
    public void verifyUserStatus(String username) {
        String userRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_INFO_FOLDER, username);
        Object redisUser = redisTemplate
                .boundHashOps(userRedisFolder)
                .get(username);

        if(!Objects.isNull(redisUser) && redisUser instanceof SurenessAccount ){
            if (((SurenessAccount) redisUser).isExcessiveAttempts()) {
                throw new ExcessiveAttemptsException("帐号被禁用帐号由于多次认证被禁用，请稍后再试 ");
            }
            if (((SurenessAccount) redisUser).isDisabledAccount()) {
                throw new DisabledAccountException("账户已禁用");
            }
        }
    }


    @Override
    public void storageUserStatus(RedisAccount account) {
        String userRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_INFO_FOLDER, account.getAppId());
        redisTemplate.boundHashOps(userRedisFolder).put(account.getAppId(), account);
        // 永不过期
        redisTemplate.persist(userRedisFolder);
    }

    @Override
    public RedisAccount loadUserStatus(String username) {
        String userRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_USER_INFO_FOLDER, username);
        Object user = redisTemplate
                .boundHashOps(userRedisFolder)
                .get(username);
        return Objects.isNull(user)?null:(RedisAccount)user;
    }

    @Override
    public void storageEnableResource(Set<String> jsonStr) {
        String enableResourceRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_RESOURCE_PATH_FOLDER,
                RedisKeyConstant.REDIS_ENABLE_RESOURCE_FOLDER);
        redisTemplate.boundHashOps(enableResourceRedisFolder).put(RedisKeyConstant.REDIS_ENABLE_RESOURCE_FOLDER,
                jsonStr);
    }

    @Override
    public Set<String> loadEnableResource() {
        String disableResourceRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_RESOURCE_PATH_FOLDER,
                RedisKeyConstant.REDIS_ENABLE_RESOURCE_FOLDER);
        Object resultObj = redisTemplate.boundHashOps(disableResourceRedisFolder)
                .get(RedisKeyConstant.REDIS_ENABLE_RESOURCE_FOLDER);
        return Objects.isNull(resultObj)?new HashSet<>(): (Set<String>) resultObj;
    }


    @Override
    public Set<String> loadEnableResourceMysqlLoad() {
        // 需要拦截的资源
        Optional<List<String>> enableResource = authResourceDao.getEnableResourcePathRoleData();
        Set<String> enableStrings = enableResource.<Set<String>>map(HashSet::new).orElseGet(() -> new HashSet<>(0));
        // 存到redis
        storageEnableResource(enableStrings);
        return enableStrings;
    }



    @Override
    public void storageDisableResource(Set<String> jsonStr) {
        String disableResourceRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_RESOURCE_PATH_FOLDER,
                RedisKeyConstant.REDIS_DISABLE_RESOURCE_FOLDER);
        redisTemplate.boundHashOps(disableResourceRedisFolder).put(RedisKeyConstant.REDIS_DISABLE_RESOURCE_FOLDER,
                jsonStr);
    }

    @Override
    public Set<String> loadDisableResource() {
        String disableResourceRedisFolder = CustomUtil.getRedisFolder(RedisKeyConstant.REDIS_RESOURCE_PATH_FOLDER,
                RedisKeyConstant.REDIS_DISABLE_RESOURCE_FOLDER);
        Object resultObj = redisTemplate.boundHashOps(disableResourceRedisFolder)
                .get(RedisKeyConstant.REDIS_DISABLE_RESOURCE_FOLDER);
        return Objects.isNull(resultObj)?new HashSet<>(): (Set<String>) resultObj;
    }


    @Override
    public Set<String> loadDisableResourceMysqlLoad() {
        Optional<List<String>> disableResource = authResourceDao.getDisableResourcePathData();
        Set<String> disabledStrings = disableResource.<Set<String>>map(HashSet::new).orElseGet(() -> new HashSet<>(0));
        storageDisableResource(disabledStrings);
        return disabledStrings;
    }



    @Override
    public Set<String> providePathData(String context) {
        // redis查询 需要拦截的资源 ( 刷新时请先存一遍redis
        Set<String> redisDataSet = this.loadEnableResource();
        if(Objects.isNull(redisDataSet) || redisDataSet.isEmpty()){
            // redis没有查询数据库（一般发生在初始化时
            redisDataSet = this.loadEnableResourceMysqlLoad();
        }
        // 返回值放入内存中由于判断   TirePathTree.PATTERN_MAP
        return SurenessCommonUtil.attachContextPath(context, redisDataSet);

    }

    @Override
    public Set<String> provideExcludedResource(String context) {
        // 在数据库查询 不被拦截的资源（即放行资源
        Set<String> redisDataSet = this.loadDisableResource();
        if(Objects.isNull(redisDataSet) || redisDataSet.isEmpty()){
            redisDataSet = this.loadDisableResourceMysqlLoad();
        }
        return SurenessCommonUtil.attachContextPath(context, redisDataSet);
    }



}
